package org.bultreebank.search_engine;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.bultreebank.document_repository.impl.IndexesDocumentsImpl;
import org.bultreebank.document_repository.interfaces.IndexesDocumentsManager;
import org.bultreebank.extractor.Extractor;
import org.bultreebank.index.schema.management.impl.IndexContextsImpl;
import org.bultreebank.index.schema.management.impl.IndexingSchemaImpl;
import org.bultreebank.index.schema.management.interfaces.IndexContextsManager;
import org.bultreebank.index.schema.management.interfaces.IndexingSchemaManager;
import org.bultreebank.index.utils.Constants;
import org.bultreebank.index.utils.Factory;
import org.bultreebank.index.utils.SystemInit;
import org.bultreebank.index.utils.Utils;
import org.bultreebank.indexing.IndexImpl;
import org.bultreebank.indexing.IndexManager;
import org.bultreebank.searching.SearchImpl;
import org.bultreebank.searching.SearchManager;
import org.w3c.dom.Document;

public class SearchEngineImpl implements SearchEngine, Constants
{
	protected IndexManager indexManager;
	protected SearchManager searchManager;
	protected IndexingSchemaManager indexSchemaManager;
	protected IndexContextsManager contextsManager;
	protected IndexesDocumentsManager indexDocumentsManager;
	protected Extractor extractor;
	protected static SearchEngine engine;
	private String[] indexingLangs;
	private String[] searchingLangs;
	public static String  repositoryPath;
	public IndexManager getIndexManager()
	{
		return indexManager;
	}
	public String[] getIndexAnalyzerLanguages()
	{
		return indexingLangs;
	}
	public String[] getSearchAnalyzerLanguages()
	{
		return searchingLangs;
	}
	public IndexesDocumentsImpl getIndexesDocumentsManager()
	{
		return (IndexesDocumentsImpl)indexDocumentsManager;
	}
	public SearchEngineImpl(String searchEnginePath) throws Exception
	{
		repositoryPath = searchEnginePath;
		indexSchemaManager = new IndexingSchemaImpl(searchEnginePath);
		this.contextsManager = new IndexContextsImpl(indexSchemaManager);
		SystemInit initManager = SystemInit.getInstance();
		String storageClass = initManager.getStorageClass();
		String storageLocation = initManager.getStorageLocation();
		Map<String, String> searchAnalyzersMap = initManager.getSearchLangMap();
		Set<String> searchLangsSet = searchAnalyzersMap.keySet();
		searchingLangs = searchLangsSet.toArray(new String[searchLangsSet.size()]);
		Map<String, String> indexAnalyzersMap = initManager.getIndexLangMap();
		Set<String> indexLangsSet = indexAnalyzersMap.keySet();
		indexingLangs = indexLangsSet.toArray(new String[indexLangsSet.size()]);
		String extractorClass = initManager.getExtractorClass();
		indexDocumentsManager = new IndexesDocumentsImpl(indexSchemaManager,
		storageClass, storageLocation);
		Map<String, Object> configMap = new HashMap<String, Object>();
		if (extractorClass != null && !extractorClass.equals(EMPTY_STR))
		{
			configMap.put(Factory.CLASS_NAME, extractorClass);
			configMap.put(Factory.PARAM_TYPES, new Class[]{IndexesDocumentsImpl.class});
			IndexesDocumentsImpl indexesDocumentsImpl = (IndexesDocumentsImpl)indexDocumentsManager;
			configMap.put(Factory.PARAM_IMPLS, new Object[]{indexesDocumentsImpl});
			extractor = (Extractor)Factory.instantiate(configMap);
		}
		
		indexManager = new IndexImpl(indexAnalyzersMap,
				contextsManager, indexDocumentsManager);
		searchManager = new SearchImpl(searchAnalyzersMap, indexSchemaManager,
				contextsManager, indexDocumentsManager);
	}
	public Extractor getExtractor()
	{
		return extractor;
	}
	
	public synchronized void indexDocument(String xmlDocument,
			String metadataDocument,
			String indexID, String docID, boolean returnReport) throws Exception
	{
		Document xmlDoc = Utils.getDocumentFromString(xmlDocument);
		Document metadataDoc = null; 
		if (metadataDocument != null)	
			metadataDoc = Utils.getDocumentFromString(metadataDocument);
		indexManager.indexDocument(xmlDoc,metadataDoc,
				indexID, docID, returnReport);
	
	}

	public synchronized void indexDocument(String docID, String metadataDoc,
			String indexID, boolean returnReport) throws Exception
	{
		Document metadataDocument = Utils.getDocumentFromString(metadataDoc);
		indexManager.indexDocument(docID,metadataDocument,
				indexID, returnReport);
	}

	public synchronized void removeDocument(String docID,
			String indexID)	throws Exception
	{
		indexManager.deleteDocFromIndex(docID, indexID);
		indexDocumentsManager.deleteDocumentForIndex(indexID, docID);
	}

	public synchronized String[] getAllRegisteredIndexes()
	throws Exception
	{
		Properties indexesProps = indexSchemaManager.getAllUser_SystemIndexes();
		Set<Object> keys = indexesProps.keySet();
		Iterator<Object> iter = keys.iterator();
		String[] result = new String[keys.size()];
		int i = 0;
		while (iter.hasNext())
		{
			String indexID = iter.next().toString();
			result[i] = indexID;
			i++;
		}
		
		return result;
	}

	public synchronized Map<String, List<String>> getFieldContent(
			String indexID,
			String fieldName, String context) throws Exception
	{
		return searchManager.getFieldContentDocument(indexID, fieldName, context);
	}

	public synchronized String[] listDocuments(String indexID) throws Exception
	{
		List<String> docs = indexDocumentsManager.getDocsForIndex(indexID);
		int size = docs.size();
		String [] result = new String[size];
		for(int i = 0; i < size; i++)
			result[i] = docs.get(i);
		return result;
	}

	public synchronized void registerIndex(String indexingSchemaDocument)
	throws Exception
	{
		Document indexSchemaDoc = Utils.getDocumentFromString(indexingSchemaDocument);
		indexSchemaManager.addIndexSchema(indexSchemaDoc);
	}

	public synchronized void removeIndex(String indexID) throws Exception
	{
		indexDocumentsManager.deleteDocsForIndex(indexID);
		indexSchemaManager.removeIndexSchema(indexID, false);
	}

	public synchronized String retrieveDocument(String docID)
	throws Exception
	{
		Document document = indexDocumentsManager.retrieveDocument(docID);
		return Utils.getStringRepresentationOfDocument(document);
	}

	public synchronized String[] search(String queryDoc,
			String indexID)throws Exception
	{
		Document query = Utils.getDocumentFromString(queryDoc);
		boolean isQueryValid = org.bultreebank.index.utils.Validator.isQueryValid(SearchEngineImpl.repositoryPath, query);
		if (!isQueryValid)
			throw new Exception ("The query documents is NOT valid!");
		Map<String, Set<String>> result = ((SearchImpl) searchManager).search(query, 
				indexID);
		String[] resultDocs = new String[result.size()];
		Iterator<String> resultIter = result.keySet().iterator();
		int i = 0;
		while(resultIter.hasNext())
		{
			StringBuffer buf = new StringBuffer();
			String context = resultIter.next();
			Set<String> docIDs = result.get(context);
			Iterator<String> docIDsIter = docIDs.iterator();
			if (docIDsIter.hasNext())
				buf.append(context + SPEC_CHAR);
			while (docIDsIter.hasNext())
				buf.append(docIDsIter.next() + WHITESPACE);
			resultDocs[i] = buf.toString();
			i++;
		}
		return resultDocs;
	}

	public static SearchEngine getInstance(String searchEnginePath) throws Exception {
		if (engine == null)
			engine = new SearchEngineImpl(searchEnginePath);
		return engine;
	}
	@Override
	public void setIndexingReportDocument()throws Exception {
		indexManager.initIndexingReportDocument();
	}
	
}
